﻿using RpcCacheClient.Interface;
using RpcClient;
using RpcHelper;
using RpcModel;
using RpcModularModel.Accredit;
using RpcModularModel.Accredit.Model;
using RpcSync.Collect;
using RpcSync.Model;
using RpcSync.Model.DB;
using RpcSyncService.Interface;

namespace RpcSyncService.Accredit
{
    [RpcClient.Attr.UnityName("db")]
    internal class DbAccreditToken : DbCacheAccredit, IAccreditToken
    {
        private IAccreditTokenCollect _TokenCollect;
        private IAccreditServer _Server;

        private AccreditToken _Token;
        public AccreditToken Token => this._Token;
        private DateTime _OverTime;

        public string CheckKey => this._Token.CheckKey;



        public DbAccreditToken(ICacheController cache,
            IAccreditTokenCollect accreditToken,
            IAccreditServer server) : base(cache)
        {
            _TokenCollect = accreditToken;
            _Server = server;
        }

        public bool Cancel()
        {
            if (this.Remove(out Guid[] keys))
            {
                this._Token.Refresh(true);
                if (!keys.IsNull())
                {
                    _Server.Cancel(keys);
                }
                return true;
            }
            return false;
        }
        public bool Remove(out Guid[] subs)
        {
            subs = this._TokenCollect.GetSubId(this.AccreditId);
            if (this._TokenCollect.Delete(this.AccreditId))
            {
                base.RmoveCache();
                return true;
            }
            return false;
        }
        public bool CheckRole(MsgSource source)
        {
            return this._Token.AccreditRole.IsExists(a => a == source.SystemType || a == source.SysGroup);
        }

        public void Create(AccreditToken token)
        {
            this._Token = token;
            TimeSpan? time = token.GetOverTime();
            if (!time.HasValue)
            {
                throw new ErrorException("accredit.expire");
            }
            AccreditTokenModel add = token.ConvertMap<AccreditToken, AccreditTokenModel>();
            DateTime now = DateTime.Now;
            add.OverTime = now.Add(time.Value);
            add.AddTime = now;
            this._TokenCollect.Add(add);
            this._OverTime = add.OverTime;
        }

        public AccreditDatum Get()
        {
            return new AccreditDatum
            {
                RoleType = this._Token.RoleType,
                StateVer = this._Token.StateVer,
                State = this._Token.State,
                Accredit = new AccreditRes
                {
                    AccreditId = this._Token.AccreditId,
                    CheckKey = this._Token.CheckKey,
                    SysGroup = this._Token.SysGroup,
                    ApplyId = this._Token.ApplyId,
                    RpcMerId = this._Token.RpcMerId,
                    Expire = this._Token.Expire
                }
            };
        }

        public bool Init(Guid accreditId)
        {
            base.InitAccreditId(accreditId);
            return this._Init();
        }
        private bool _Init()
        {
            if (base.TryGet(out AccreditTokenDatum token))
            {
                if (token.AccreditId == Guid.Empty || token.OverTime <= DateTime.Now)
                {
                    return false;
                }
                this._OverTime = token.OverTime;
                this._Token = token.ConvertMap<AccreditTokenDatum, AccreditToken>();
                return true;
            }
            AccreditTokenDatum datum = _TokenCollect.Get(this.AccreditId);
            if (datum == null)
            {
                datum = new AccreditTokenDatum();
                base.SetCache(datum);
                return false;
            }
            base.SetCache(datum);
            if (datum.OverTime <= DateTime.Now)
            {
                return false;
            }
            this._OverTime = datum.OverTime;
            this._Token = datum.ConvertMap<AccreditTokenDatum, AccreditToken>();
            return true;
        }

        public TimeSpan? Refresh()
        {
            if (this._Token.Expire.HasValue)
            {
                return null;
            }
            TimeSpan time = AccreditHelper.GetDefAccreditTime();
            this._OverTime = DateTime.Now.Add(time);
            _TokenCollect.SetOverTime(this.AccreditId, this._OverTime);
            base.RmoveCache();
            return time;
        }


        public bool Set(SetAccredit obj)
        {
            this._Token.AccreditRole = obj.AccreditRole ?? Array.Empty<string>();
            this._Token.State = obj.State;
            this._Token.Expire = !obj.Expire.HasValue ? null : DateTime.Now.AddSeconds(obj.Expire.Value);
            this._Token.StateVer += 1;
            return this._SaveToken();
        }

        public bool SetState(string state)
        {
            this._Token.State = state;
            this._Token.StateVer += 1;
            return this._SaveToken();
        }
        private bool _SaveToken()
        {
            TimeSpan? time = this._Token.GetOverTime();
            if (!time.HasValue)
            {
                throw new ErrorException("accredit.Invalid");
            }
            this._OverTime = DateTime.Now.Add(time.Value);
            AccreditTokenSet set = this._Token.ConvertMap<AccreditToken, AccreditTokenSet>();
            set.OverTime = this._OverTime;
            if (_TokenCollect.Set(this.AccreditId, set))
            {
                base.RmoveCache();
                this._Token.Refresh(true);
                return true;
            }
            else if (!this._Init())
            {
                throw new ErrorException("accredit.Invalid");
            }
            return false;
        }
    }
}
